package com.yusvn.scm.common.kit;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.wall.WallFilter;
import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.CaseInsensitiveContainerFactory;
import com.jfinal.plugin.activerecord.DbKit;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.activerecord.dialect.MysqlDialect;
import com.jfinal.plugin.activerecord.dialect.OracleDialect;
import com.jfinal.plugin.activerecord.dialect.PostgreSqlDialect;
import com.jfinal.plugin.activerecord.dialect.SqlServerDialect;
import com.jfinal.plugin.activerecord.dialect.Sqlite3Dialect;
import com.jfinal.plugin.druid.DruidPlugin;


/**
 * 数据库连接类
 * @author liugz
 *
 */
public class DblinkKit {
	public static final String DBID = "dbid";	            // 数据库连接编号
	public static final String DBTYPE = "dbtype";			// 数据库类型
	public static final String DBHOST = "dbhost";           // 数据库服务器主机ip地址
	public static final String DBPORT = "dbport";           // 数据库服务器主机端口
	public static final String DBNAME = "dbname";           // 数据库名称
	public static final String DBUSERNAME = "dbusername";	// 数据库用户名
	public static final String DBPASSWORD = "dbpassword";	// 数据库密码
	public static final String DBDRIVER = "dbdriver";		// 数据库驱动器（如果填了数据库类型，这里可为空）
	public static final String DBURL = "dburl";			    // 数据库连接地址（如果不为空则优先取此值）
	public static final String DBMAXACTIVE = "dbmaxactive"; // 数据库最大活跃连接数
	public static final String ISCASE = "iscase"; 			// 字段是否大小写，0 是大写, 1是小写, -1不写是区分大小写
	
	/**
	 * 获取数据库连接url
	 * 根据数据库类型dbtype获取数据库url，先判断dburl是否空
	 * 各数据连接URL:
	 * jdbc:mysql://127.0.0.1:3306/demo1?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
	 * jdbc:sqlserver://127.0.0.1:1433;DatabaseName=demo1;encrypt=false
	 * jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=demo1
	 * jdbc:oracle:thin:@127.0.0.1:1521:demo1
	 * jdbc:sqlite:E:\\dev\sqlite\demo1.db
	 * jdbc:postgresql://127.0.0.1:5432/demo1
	 * jdbc:odbc:在odbc数据源中添加的名称  (注：access使用电脑的odbc数据源管理建立连接)
	 */
	public static String getDburl(String dbtype, String dbhost, String dbport, String dbname, String dburl) {
		if(StrKit.notBlank(dburl)) {
			return dburl;
		}
		
		switch (dbtype) {
		case "mysql":
			dburl = "jdbc:mysql://"+dbhost+":"+dbport+"/"+dbname+"?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8";
			break;
		case "oracle":
			dburl = "jdbc:oracle:thin:"+dbhost+":"+dbport+":"+dbname+"";
			break;
		case "sqlserver":
			dburl = "jdbc:sqlserver://"+dbhost+":"+dbport+";DatabaseName="+dbname+""; ////2005及以上版本
			break;
		case "sql2000":
			dburl = "jdbc:microsoft:sqlserver://"+dbhost+":"+dbport+";DatabaseName="+dbname+""; //2000版本
			break;
		case "access":
			dburl = "jdbc:odbc:"+dbhost+"";
			break;
		case "sqlite":
			dburl = dbhost;
			break;
		case "postgresql":
			dburl = "jdbc:postgresql://"+dbhost+":"+dbport+"/"+dbname+"";
			break;
		}
		return dburl;
	}
	
	/**
	 * 获取数据库驱动器类型
	 * 根据数据库类型dbtype获取数据库驱动器类型，先判断dbdriver是否空
	 * @param String dbtype 数据库类型(mysql、sqlserver、oracle、sqlite、postgresql...)
	 * @return Ret msg
	*/
	public static String getDbdriver(String dbtype, String dbdriver) {
		if(StrKit.notBlank(dbdriver)) {
			return dbdriver;
		}
		
		switch (dbtype) {
		case "mysql":
			dbdriver = "com.mysql.jdbc.Driver";
			break;
		case "oracle":
			dbdriver = "oracle.jdbc.driver.OracleDriver";
			break;
		case "sqlserver":
			dbdriver = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; ////2005及以上版本
			break;
		case "sql2000":
			dbdriver = "com.microsoft.jdbc.sqlserver.SQLServerDriver"; //2000版本
			break;
		case "access":
			dbdriver = "sun.jdbc.odbc.JdbcOdbcDriver";
			break;
		case "sqlite":
			dbdriver = "org.sqlite.JDBC";
			break;
		case "postgresql":
			dbdriver = "org.postgresql.Driver";
			break;
		}
		return dbdriver;
	}
	
	/**
	 * 检测数据库连接是否有效
	 * @param Kv 
	 * @return Ret msg
	 */
	public static Ret checkDblink(Record rec) {
		if (!StrKit.notNull(rec)) {
			return Ret.fail("msg", "数据库连接参数空。");
		}
		
		String dbtype = rec.getStr(DBTYPE);		// 数据库类型
		String dbdriver = rec.getStr(DBDRIVER); // 数据库驱动器
		String dburl = rec.getStr(DBURL);  		// 数据库连接地址
		String dbusername = rec.getStr(DBUSERNAME);// 数据库用户名
		String dbpassword = rec.getStr(DBPASSWORD);// 数据库密码
		// 获取数据库驱动器（如dbtype为空，则根据url地址自动获取数据库类型)
		if(StrKit.isBlank(dbtype)) {
			dbtype = autoGetDbtype(dburl);
		}
		dbdriver = getDbdriver(dbtype, dbdriver);
		// 数据库密码解密
		dbpassword = EncryptKit.decode(EncryptKit.KEY, dbpassword);
		
		Connection con = null;
		try {
			Class.forName(dbdriver);
			con = DriverManager.getConnection(dburl, dbusername, dbpassword);
			return con == null ? Ret.fail() : Ret.ok();
		} catch (Exception e) {
			return Ret.fail("msg", "数据库连接异常。");
		}
	}
	
	/**
	 * 判断数据库连接是否存在
	 * @param dblinknum
	 * @return
	 */
	public static Ret getDblink(String dbid) {
		if(StrKit.notNull(DbKit.getConfig(dbid)) ) { //&& DbKit.getConfig().getName().equals(dbid) 
			return Ret.ok("msg", "已成功连接数据库。");
		}
		return Ret.fail();
	}
	
	/**
	 * 根据url地址自动获取数据库类型
	 */
	private static String  autoGetDbtype(String url){
		if(url.startsWith("jdbc:sqlserver")){
			return "sqlserver";
		}else if(url.startsWith("jdbc:mysql")){
			return "mysql";
		}else if(url.startsWith("jdbc:oracle")){
			return "oracle";
		}else if(url.startsWith("jdbc:microsoft:sqlserver")){
			return "sql2000";
		}else if(url.startsWith("jdbc:postgresql")){
			return "postgresql";
		}
		return "select 1";
	}
	
	/**
	 * 获取数据库连接
	 * @param Kv
	 * @return Ret msg
	 */
	public static Ret getDblink(Record rec) {
		if (!StrKit.notNull(rec)) {
			return Ret.fail("msg", "数据库连接参数空。");
		}
		
		String dbid = rec.getStr(DBID); // 数据库连接ID
		String dbtype = rec.getStr(DBTYPE); // 数据库类型
		String dbdriver = rec.getStr(DBDRIVER); // 数据库驱动器
		String dburl = rec.getStr(DBURL); // 数据库连接地址
		String dbusername = rec.getStr(DBUSERNAME); // 数据库用户名
		String dbpassword = rec.getStr(DBPASSWORD); // 数据库密码
		int dbmaxactive = StrKit.isBlank(rec.getStr(DBMAXACTIVE)) ? 100 : rec.getInt(DBMAXACTIVE); // 数据库最大活跃连接数
		int iscase = StrKit.isBlank(rec.getStr(ISCASE)) ? -1 : rec.getInt(ISCASE); //字段大小写，0 是大写, 1是小写, -1不写是区分大小写
		
		// 获取数据库驱动器（如dbtype为空，则根据url地址自动获取数据库类型)
		if(StrKit.isBlank(dbtype)) {
			dbtype = autoGetDbtype(dburl);
		}		
		dbdriver = getDbdriver(dbtype, dbdriver);
		
		// 数据库密码解密
		dbpassword = EncryptKit.decode(EncryptKit.KEY, dbpassword);
		
		// 判断数据库连接是否存在
		getDblink(dbid); 
		
		try {
			// 判断数据库连接是否有效
			Ret checkRet = checkDblink(rec);
			if (checkRet.isFail()) {
				return checkRet;
			}

			// 配置DruidPlugin数据库连接池
			DruidPlugin dp = new DruidPlugin(dburl, dbusername, dbpassword, dbdriver);
			dp.set(1, 1, dbmaxactive); // 设置初始连接池大小、最小空闲连接数、最大活跃连接数
			
			switch (dbtype) {
			case "mysql":
				WallFilter wallFilter = new WallFilter(); // 加强数据库安全
				wallFilter.setDbType("mysql");
				dp.addFilter(wallFilter);
				dp.addFilter(new StatFilter());	// 添加StatFilter才会有统计数据
			case "oracle":
				dp.setMaxPoolPreparedStatementPerConnectionSize(100); //在Druid中，不会存在Oracle下PSCache占用内存过多的问题，可以把这个数值配置大一些，比如说100
			}

			// 配置ActiveRecord数据库连接池插件
			ActiveRecordPlugin arp = new ActiveRecordPlugin(dbid, dp);

			// 设置数据库方言
			switch (dbtype) {
			case "mysql":
				arp.setDialect(new MysqlDialect());
				break;
			case "sqlserver":
				arp.setDialect(new SqlServerDialect());
				break;
			case "oracle":
				arp.setDialect(new OracleDialect());
				//arp.setContainerFactory(new CaseInsensitiveContainerFactory(true)); // 配置属性名(字段名)大小写不敏感容器工厂,true小写,false大写
				break;
			case "sqlite":
				arp.setDialect(new Sqlite3Dialect());
				break;
			case "postgresql":
				arp.setDialect(new PostgreSqlDialect());
				break;
			}

			if (dbtype.equals("mysql")) {
				arp.setDialect(new MysqlDialect());
			} else if (dbtype.equals("sqlserver")) {
				arp.setDialect(new SqlServerDialect());
			} else if (dbtype.equals("oracle")) {
				arp.setDialect(new OracleDialect());
			} else if (dbtype.equals("sqlite")) {
				arp.setDialect(new Sqlite3Dialect());
			} else if (dbtype.equals("postgresql")) {
				arp.setDialect(new PostgreSqlDialect());
			}

			// 配置属性名(字段名)大小写不敏感容器工厂（false 是大写, true是小写, 不写是区分大小写）
			if(iscase>=0) {
				boolean i = iscase==0 ? false : true;
				arp.setContainerFactory(new CaseInsensitiveContainerFactory(i));
			}else {
				arp.setContainerFactory(new CaseInsensitiveContainerFactory());
			}

			if(dp.start() && arp.start()) {
				return Ret.ok("msg", "连接数据库成功。");
			}else {
				throw new SQLException("数据库连接池或管理器启动失败。"); // 抛出异常，不影响程序往下执
			}
			
		} catch (Exception e) {
			return Ret.fail("msg", "连接数据库失败。");
		}
		
	}
	
	/**
	 * 关闭数据库连接
	 */
	public static void stopDblink(String configName) {
		DbKit.removeConfig(configName);
	}
	
}
